标签
内存
字数
1897 字
阅读时间
10 分钟
import com.commnetsoft.pub.monitor.memory.MemoryMonitorUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
/**
* 内存监控,出现内存不够时输出相应日志
* @author Brack.zhu
* @date 2019/11/5
*/
@Component
public class MemoryMonitorRunner implements CommandLineRunner {
private Logger log = LoggerFactory.getLogger(MemoryMonitorRunner.class);
@Override
public void run(String... args) throws Exception {
log.info("JVM内存监听器启动中...");
//Mbean 监听器 MemoryMonitorUtil.addUsageListener();
//使用GC监听器监听JVM内存
MemoryMonitorUtil.startGcUsageMonitor();
log.info("JVM内存监听器完成.");
}
}CommandLineRunner接口是SpringBoot提供的一种简单的实现启动时执行某个任务的方案,添加一个model并实现CommandLineRunner接口,实现功能的代码放在实现的run方法中
java
package com.commnetsoft.pub.monitor.memory;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.util.List;
import javax.management.NotificationEmitter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.commnetsoft.pub.monitor.memory.gc.GCFinalizeObject;
import com.commnetsoft.pub.monitor.memory.gc.GcNotificationHandler;
import com.commnetsoft.pub.monitor.memory.gc.UsageThresholdGcNotificationHandler;
import com.commnetsoft.pub.monitor.memory.threshold.MonitorListener;
import com.commnetsoft.pub.monitor.memory.threshold.NotificationHandler;
import com.commnetsoft.pub.monitor.memory.threshold.UsageThresholdNotificationHandler;
/**
* 内存监控工具类,提供各内存块(Code Cache\Eden Space\Survivor Space\Tenured Gen)阀值监控和GC堆内存监控
*
*
* Eden Space (heap) 内存最初从这个线程池分配给大部分对象。
*
* Survivor Space (heap) 用于保存在eden space内存池中经过垃圾回收后没有被回收的对象。
*
* Tenured Generation (heap) 用于保持已经在survivor space内存池中存在了一段时间的对象。
*
* Permanent Generation (non-heap)
* 保存虚拟机自己的静态(reflective)数据,例如类(class)和方法(method)对象。Java虚拟机共享这些类数据。
* 这个区域被分割为只读的和只写的。 内存池“Perm Gen [shared-rw]”:
* 包含所有虚拟机自身的反射数据(如类和方法对象)的内存池,类数据共享可读可写区。
*
* 内存池“Perm Gen [shared-ro]”: 包含所有虚拟机自身的反射数据(如类和方法对象)的内存池,类数据共享只读区。
*
* Code Cache (non-heap) HotSpot Java虚拟机包括一个用于编译和保存本地代码(native
* code)的内存,叫做“代码缓存区”(code cache)。
*
*
* @author Brack.zhu
* @date 2017-3-7
*/
public class MemoryMonitorUtil {
private static Logger log = LoggerFactory.getLogger(MemoryMonitorUtil.class);
private static MonitorListener mmListener;
/**
* 增加一个默认各内存池阀值监听器
*
* @return
*/
public static boolean addUsageListener() {
NotificationHandler notificationHandler = new UsageThresholdNotificationHandler();
MonitorListener mListener = new MonitorListener(notificationHandler);
return addUsageListener(mListener);
}
/**
* 增加各内存池阀值监听器
*
* @param mListener
* 监听器
* @return
*/
public static boolean addUsageListener(MonitorListener mListener) {
boolean result = false;
try {
MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
NotificationEmitter emitter = (NotificationEmitter) mbean;
List<MemoryPoolMXBean> poolMbean = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean pool : poolMbean) {
try {
boolean rs=mListener.setUsageThreshold(pool);
if(rs){
emitter.addNotificationListener(mListener, null, pool);
}
} catch (Exception e) {
log.error("内存池("+pool.getName()+":"+pool.getType()+")监听器增加异常:", e);
}
}
MemoryMonitorUtil.mmListener = mListener;
result = true;
} catch (Exception e) {
log.error("增加各内存池阀值监听器异常:", e);
}
return result;
}
/**
* 移除各内存池阀值监听器
*
* @return
*/
public static boolean removeUsageListener() {
boolean result = false;
if (null != mmListener) {
try {
MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
NotificationEmitter emitter = (NotificationEmitter) mbean;
List<MemoryPoolMXBean> poolMbean = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean pool : poolMbean) {
try {
boolean rs=mmListener.clearUsageThreshold(pool);
if(rs){
emitter.removeNotificationListener(mmListener, null, pool);
}
} catch (Exception e) {
log.error("内存池("+pool.getName()+":"+pool.getType()+")监听器移除异常:", e);
}
}
mmListener = null;
result = true;
} catch (Exception e) {
log.error("移除各内存池阀值监听器异常:", e);
}
}
return result;
}
/**
* 启动GC堆内存监控,堆内存剩余20%触发Handler
*
* @return
*/
public static boolean startGcUsageMonitor() {
GcNotificationHandler gcHandler = new UsageThresholdGcNotificationHandler();
return startGcUsageMonitor(20, gcHandler);
}
/**
* 启动GC堆内存监控
*
* @param per
* 堆内存剩余百分比,达到这个值触发Handler;取值范围:1-99
* @return
*/
public static boolean startGcUsageMonitor(int per) {
GcNotificationHandler gcHandler = new UsageThresholdGcNotificationHandler();
return startGcUsageMonitor(per, gcHandler);
}
/**
* 启动GC堆内存监控
*
* @param per
* per 堆内存剩余百分比,达到这个值触发Handler;取值范围:1-99
* @param gcHandler
* 通知处理器
* @return
*/
public static boolean startGcUsageMonitor(int per, GcNotificationHandler gcHandler) {
return GCFinalizeObject.start(per, gcHandler);
}
/**
* 停止GC堆内存监控
*
* @return
*/
public static boolean stopGcUsageMonitor() {
return GCFinalizeObject.stop();
}
}
package com.commnetsoft.pub.monitor.memory.threshold;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.text.DecimalFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 内存池阀值通知处理类
*
* @author Brack.zhu
* @date 2017-3-7
*/
public class UsageThresholdNotificationHandler implements NotificationHandler {
private DecimalFormat df = new DecimalFormat("0.00");
private Logger log = LoggerFactory.getLogger(UsageThresholdNotificationHandler.class);
@Override
public void handleNotification(MemoryPoolMXBean memoryPool) {
String name = memoryPool.getName();
MemoryUsage mUsage = memoryPool.getUsage();
long max = mUsage.getMax() / 1048576; // 1024 * 1024;
long used = mUsage.getUsed() / 1048576; // 1024 * 1024;
long free = max - used;
float proportion = (float) free / (float) max;
String proportionStr = df.format(proportion);
log.warn("!警告!:(JVM)可用内存不足;可以设置更大内存-重启或者让研发检查;" + name + ":总量=" + max + "M,可用总量=" + free + "M,可用比例=" + proportionStr);
}
}
package com.commnetsoft.pub.monitor.memory.threshold;
import java.lang.management.MemoryPoolMXBean;
/**
* 内存池阀值通知处理接口
* @author Brack.zhu
* @date 2017-3-7
*/
public interface NotificationHandler {
/**
* 处理通知
* @param memoryPool 内存池对象
*/
public void handleNotification(MemoryPoolMXBean memoryPool);
}
package com.commnetsoft.pub.monitor.memory.threshold;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import javax.management.Notification;
import javax.management.NotificationListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 内存池阀值监听类
*
* @author Brack.zhu
* @date 2017-3-7
*/
public class MonitorListener implements NotificationListener {
private NotificationHandler notificationHandler = null;
private Logger log = LoggerFactory.getLogger(MonitorListener.class);
public MonitorListener(NotificationHandler notificationHandler) {
this.notificationHandler = notificationHandler;
}
/**
* 内存池阀值设置
*
* @param mPool
* @return
*/
public boolean setUsageThreshold(MemoryPoolMXBean mPool) {
boolean result=false;
try {
if (mPool.isUsageThresholdSupported() && "Tenured Gen".equalsIgnoreCase(mPool.getName())) {// 老年代
MemoryUsage mUsage = mPool.getPeakUsage();
long max = mUsage.getMax();
long threshold = (long) (max * 0.8);//阀值为最大值的80%
mPool.setUsageThreshold(threshold);
result=true;
}
} catch (Exception e) {
log.error("内存池(" + mPool.getName()+":"+mPool.getType() + ")阀值设置异常:", e);
}
return result;
}
/**
* 内存池阀值清除
*
* @param mPool
* @return
*/
public boolean clearUsageThreshold(MemoryPoolMXBean mPool) {
boolean result=false;
try {
if (mPool.isUsageThresholdSupported()&& "Tenured Gen".equalsIgnoreCase(mPool.getName())) {// 老年代
mPool.setUsageThreshold(0);// 设置为零,则将禁用使用量阈值超过检查。
result=true;
}
} catch (Exception e) {
log.error("内存池(" + mPool.getName()+":"+mPool.getType() + ")阀值清除异常:", e);
}
return result;
}
/**
* @param notification
* @param handback
* MemoryPoolMXBean内存池对象
*/
@Override
public void handleNotification(Notification notification, Object handback) {
if (null != notificationHandler) {
if (handback instanceof MemoryPoolMXBean) {
MemoryPoolMXBean mPool = (MemoryPoolMXBean) handback;
notificationHandler.handleNotification(mPool);
}
} else {
log.error("notificationHandler 对象为空!");
}
}
}
package com.commnetsoft.pub.monitor.memory.gc;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <pre>
* GC调用后最后实现公用方法对象---可用内存与总内存的比例,可用低于0.2的情况进行日志输出
* 使用方法:构造一次该对象即可,如下
* public static void main(){
* ....
* new GCFinalizeObject();
* ....
* }
* </pre>
* @author Black.zhu
* @date 2012-7-12
*/
public class GCFinalizeObject {
private static boolean runFlag=true;
private static float gcUsageThreshold=0.20f;
private static GcNotificationHandler gcUsageMonitorHandler;
private static Logger log = LoggerFactory.getLogger(GCFinalizeObject.class);
@Override
protected void finalize() throws Throwable {
try {
if(null!=gcUsageMonitorHandler){
MemoryMXBean memory=ManagementFactory.getMemoryMXBean();
MemoryUsage usage=memory.getHeapMemoryUsage();
long max=usage.getMax()/1048576; //1024 * 1024;
long used=usage.getUsed()/1048576; //1024 * 1024;
long free=max-used;
float proportion = (float)free /(float) max;
int compareRs = Float.compare(gcUsageThreshold, proportion);// 不能小于0.2
if (compareRs > 0) {
gcUsageMonitorHandler.handleNotification(max,free,proportion);
}
}else{
log.error("GcNotificationHandler 对象为空!");
}
} catch (Exception e) {
log.error("GCFinalizeObject finalize 异常:", e);
}
if(runFlag){
new GCFinalizeObject();
}
super.finalize();
}
/**
* 启动GC堆内存监控
* @param per 堆内存剩余百分比,达到这个值触发Handler;取值范围:1-99
* @param handler
* @return
*/
public static boolean start(int per,GcNotificationHandler handler){
gcUsageMonitorHandler=handler;
if(per<100&&per>=1){
gcUsageThreshold=per/100.0F;
}
new GCFinalizeObject();
return true;
}
/**
* 停止GC堆内存监控
* @return
*/
public static boolean stop(){
runFlag=false;
gcUsageMonitorHandler=null;
return true;
}
}
package com.commnetsoft.pub.monitor.memory.gc;
/**
* GC堆内存监控通知处理接口
* @author Brack.zhu
* @date 2017-3-7
*/
public interface GcNotificationHandler {
/**
* 通知处理
* @param max 最大堆内存
* @param free 可用堆内存
* @param proportion 可用堆内存比率
*/
public void handleNotification(long max,long free,float proportion);
}
package com.commnetsoft.pub.monitor.memory.gc;
import java.text.DecimalFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* GC堆内存监控通知处理类
*
* @author Brack.zhu
* @date 2017-3-7
*/
public class UsageThresholdGcNotificationHandler implements GcNotificationHandler {
private DecimalFormat df = new DecimalFormat("0.00");
private long lastHandlerTime=0;
private Logger log = LoggerFactory.getLogger(UsageThresholdGcNotificationHandler.class);
@Override
public void handleNotification(long max, long free, float proportion) {
long now=System.currentTimeMillis();
if((now-lastHandlerTime)>1500){//至少间隔1.5秒输出
lastHandlerTime=now;
String proportionStr = df.format(proportion);
log.warn("!警告!:(JVM)可用内存不足;可以设置更大内存-重启或者让研发检查;总量=" + max + "M,可用总量=" + free + "M,可用比例=" + proportionStr);
}
}
}